// bsla_printf.h                                                      -*-C++-*-
#ifndef INCLUDED_BSLA_PRINTF
#define INCLUDED_BSLA_PRINTF

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a macro to indicate `printf`-style arguments.
//
//@MACROS:
//  BSLA_PRINTF(FMTIDX, STARTIDX): validate `printf` format and arguments
//  BSLA_PRINTF_IS_ACTIVE:         defined if `BSLA_PRINTF` is active
//
//@SEE_ALSO: bsla_annotations
//
//@DESCRIPTION: This component provides a preprocessor macro that allows the
// designation of a given function argument as a `printf`-style format string,
// and arguments starting at a certain index in the argument list to be
// formatted according to that string.
//
///Macro Reference
///---------------
//: `BSLA_PRINTF(FMTIDX, STARTIDX)`:
//:     This annotation instructs the compiler to perform additional
//:     compile-time checks on so-annotated functions that take `printf`-style
//:     arguments, which should be type-checked against a format string.
//:
//     - The `FMTIDX` parameter is the one-based index to the `const char *`
//       format string.  The `STARTIDX` parameter is the one-based index to
//       the first variable argument to type-check against that format string.
//       For example:
// ```
// extern int my_printf(void *obj, const char *format, ...) BSLA_PRINTF(2, 3);
// ```
//:
//: `BSLA_PRINTF_IS_ACTIVE`:
//:     The macro `BSLA_PRINTF_IS_ACTIVE` is defined if `BSLA_PRINTF` expands
//:     to something with the desired effect; otherwise `BSLA_PRINTF_IS_ACTIVE`
//:     is not defined and `BSLA_PRINTF` expands to nothing.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: `printf`-Like Function That Returns a `bsl::string` by Value
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// First, we define a function, `strPrintf`, that takes a variable number of
// arguments.  The second argument is the format string, and we annotate it
// with `BSLA_PRINTF`:
// ```
// std::string strPrintf(size_t *numChars, const char *format, ...)
//                                                          BSLA_PRINTF(2, 3);
// std::string strPrintf(size_t *numChars, const char *format, ...)
//     // Do a 'sprintf'-style write to a 'std::string' and return the string
//     // by value.  Ensure that the write can't overflow unless memory or
//     // address space is exhausted.  The specified '*numChars' is the number
//     // of characters written, the specified 'format' is the 'printf'-style
//     // format string, and the specified '...' is the variable-length list
//     // of arguments to be formatted.
// {
//     std::string ret = " ";
//
//     va_list ap;
//     va_start(ap, format);
//
//     // 'vsnprintf' returns the number of characters that WOULD have been
//     // written (not including the terminating '\0') had the buffer been
//     // long enough.
//
//     *numChars = ::vsnprintf(&ret[0], 1, format, ap);
//     va_end(ap);
//
//     ret.resize(*numChars + 1);
//
//     va_start(ap, format);
//     *numChars = ::vsnprintf(&ret[0], *numChars + 1, format, ap);
//     va_end(ap);
//
//     BSLS_ASSERT(::strlen(ret.c_str()) == *numChars);
//
//     ret.resize(*numChars);
//     return ret;
// }
// ```
// Then, in `main`, we call the function correctly a couple of times:
// ```
//     size_t len;
//     std::string s;
//
//     s = strPrintf(&len, "%s %s %s %g\n", "woof", "meow", "arf", 23.5);
//     assert("woof meow arf 23.5\n" == s);
//     assert(19 == len);
//     assert(len == s.length());
//
//     s = strPrintf(&len, "%s %s %s %s %s %s %s %s %s\n",
//                            "The", "rain", "in", "Spain", "falls", "mainly",
//                                                      "in", "the", "plain");
//     assert("The rain in Spain falls mainly in the plain\n" == s);
//     assert(44 == len);
//     assert(len == s.length());
// ```
// Now, we call it with too many arguments and of the wrong type:
// ```
//     s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
// ```
// Finally, we observe the compiler warnings with clang:
// ```
// .../bsla_printf.t.cpp:328:41: warning: format specifies type 'int' but the
// argument has type 'const char *' [-Wformat]
//     s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
//                          ~~         ^~~~~
//                          %s
// .../bsla_printf.t.cpp:328:48: warning: format specifies type 'double' but
// the argument has type 'int' [-Wformat]
//     s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
//                             ~~             ^~
//                             %d
// .../bsla_printf.t.cpp:328:52: warning: format specifies type 'double' but
// the argument has type 'int' [-Wformat]
//     s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
//                                ~~              ^~
//                                %d
// .../bsla_printf.t.cpp:328:56: warning: data argument not used by format
// string [-Wformat-extra-args]
//     s = strPrintf(&len, "%c %f %g", "arf", 27, 32, 65, 27);
//                         ~~~~~~~~~~                 ^
// ```

#include <bsls_platform.h>

                       // =============================
                       // Checks for Pre-Defined macros
                       // =============================

#if defined(BSLA_PRINTF)
#error BSLA_PRINTF is already defined!
#endif

#if defined(BSLA_PRINTF_IS_ACTIVE)
#error BSLA_PRINTF_IS_ACTIVE is already defined!
#endif

                       // =========================
                       // Set macros as appropriate
                       // =========================


#if defined(BSLS_PLATFORM_CMP_GNU) && !defined(BSLS_PLATFORM_CMP_CLANG)
    #define BSLA_PRINTF(fmt, arg) __attribute__((format(gnu_printf, fmt, arg)))

    #define BSLA_PRINTF_IS_ACTIVE 1
#elif defined(BSLS_PLATFORM_CMP_CLANG) ||                                     \
      defined(BSLS_PLATFORM_CMP_HP)    ||                                     \
      defined(BSLS_PLATFORM_CMP_IBM)
    #define BSLA_PRINTF(fmt, arg) __attribute__((format(printf, fmt, arg)))

    #define BSLA_PRINTF_IS_ACTIVE 1
#else
    #define BSLA_PRINTF(fmt, arg)
#endif

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
